/* eslint-disable max-len */
/* eslint-disable no-restricted-properties */
/* eslint-disable no-mixed-operators */
const inside = require('point-in-polygon'); // 并不要求是凸多边形！

// 关键SimplePoint并不显式的调用Release，所以无法使用内存池
class SimplePoint {
	x: number;

	y: number;

	constructor(x: number, y: number) {
		this.x = x;
		this.y = y;
	}
}

const dir2Angle = [45, 135, 225, 315, 90, 180, 270, 0];
const dir2pos = [
	[1, 1],
	[-1, 1],
	[-1, -1],
	[1, -1],
	[0, 1],
	[-1, 0],
	[0, -1],
	[1, 0],
];

namespace miscUtils {
	const sqrt2 = 1.414;

	export function clamp(v: number, min: number, max: number): number {
		if (min != null && v < min) {
			return min;
		}
		if (max != null && v > max) {
			return max;
		}
		return v;
	}

	// valid is 0,2,4,6,7
	// cut is 1,3,5
	export function getMirrordir(v: number): number {
		let v2 = v;
		if (v === 1) {
			v2 = 0;
		} else if (v === 3) {
			v2 = 2;
		} else if (v === 5) {
			v2 = 7;
		}
		return v2;
	}

	export function isMirrordir(v: number): boolean {
		return getMirrordir(v) !== v;
	}

	export function leftPad(str: string | number | number[], size: number, character: string): string {
		let result = String(str);
		while (result.length < size) {
			result = character + result;
		}
		return result;
	}

	export function stringPad(id: number, size: number): string {
		return leftPad(id, size, '0');
	}

	export function server2clientPt(pt: SimplePoint): SimplePoint {
		return new SimplePoint(pt.x * 20, pt.y * 20);
	}
	export function server2client(x: number, y: number): [number, number] {
		return [x * 20, y * 20];
	}
	export function server2clientCenter(x: number, y: number): [number, number] {
		return [x * 20 + 10, y * 20 + 10];
	}
	export function client2serverPt(pt: SimplePoint): SimplePoint {
		return new SimplePoint(Math.floor(pt.x / 20), Math.floor(pt.y / 20));
	}
	export function client2server(x: number, y: number): [number, number] {
		return [Math.floor(x / 20), Math.floor(y / 20)];
	}

	/* ```
	#				6
	#		   2		3
	#		5				7
	#			1		0
	#				4
	#
	``` */
	export function backDir(dir: number): number {
		if (dir < 4) {
			dir += 2;
			if (dir >= 4) dir -= 4;
			return dir;
		}

		dir += 2;
		if (dir >= 8) dir -= 4;
		return dir;
	}

	// map有透视的关系！
	export function dirMappos(x: number, y: number, dir: number, dist: number): [number, number] {
		return [x + dir2pos[dir][0] * dist, y + (dir2pos[dir][1] * dist) / 2];
	}

	export function dirPos(x: number, y: number, dir: number, dist: number): [number, number] {
		return [x + dir2pos[dir][0] * dist, y + dir2pos[dir][1] * dist];
	}

	// export function dir_pos2(x: number, y: number, dir: number, dist: number) : [number,number]{
	//     return [x + dir2pos[dir][0] * dist, y + dir2pos[dir][1] * dist * 2]
	// }

	export function calDir4(sx: number, sy: number, ex: number, ey: number): number {
		if (ex >= sx) {
			if (ey >= sy) return 0;
			return 3;
		}

		if (ey >= sy) return 1;
		return 2;
	}

	// 椭圆而不是正圆
	// const tg225 = Math.tan(22.5 * (Math.PI / 180));
	const tg225 = Math.tan(15 * (Math.PI / 180));

	export function calDir8(sx: number, sy: number, ex: number, ey: number): number {
		const dx = ex - sx;
		const dy = ey - sy;
		if (dx === 0) return dy > 0 ? 4 : 6;
		const tg = Math.abs((dy * sqrt2) / dx);
		if (tg < tg225) {
			return dx > 0 ? 7 : 5;
		}
		if (tg < 1 / tg225) {
			return calDir4(sx, sy, ex, ey);
		}

		return dy > 0 ? 4 : 6;
	}

	export function calDir(sx: number, sy: number, ex: number, ey: number, maxdir: number): number {
		// /20 会导致dir反复变抖动
		// const [sx, sy] = client2server(_sx, _sy);
		// const [ex, ey] = client2server(_ex, _ey);
		if (maxdir === 8) {
			return calDir8(sx, sy, ex, ey);
		}
		if (maxdir === 4) {
			return calDir4(sx, sy, ex, ey);
		}
		return 0;
	}

	export function calcAngle(sx: number, sy: number, ex: number, ey: number): number {
		const dx = ex - sx;
		const dy = ey - sy;
		// if (dy === 0) return dx > 0 ? 0 : 180;
		const angle = Math.atan2(dy, dx);
		return (angle / Math.PI) * 180;
	}

	export function slope(x: number, y: number, x1: number, y1: number): number {
		// 坐标系是颠倒的
		let angle = Math.atan2(y1 - y, x1 - x);
		if (angle < 0) {
			angle = 2 * Math.PI + angle;
		}
		return angle;
	}

	export function distanceIn(x: number, y: number, x1: number, y1: number, In: number): boolean {
		return distance2Y(x, y, x1, y1) <= In * In;
	}

	export function distanceOut(
		x: number,
		y: number,
		x1: number,
		y1: number,
		Out: number,
	): boolean {
		return !distanceIn(x, y, x1, y1, Out);
	}

	export function distance(x: number, y: number, x1: number, y1: number): number {
		return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
	}
	// 不求平方根
	export function distance2(x: number, y: number, x1: number, y1: number): number {
		return (x - x1) * (x - x1) + (y - y1) * (y - y1);
	}

	export function distanceY(x: number, y: number, x1: number, y1: number): number {
		return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1) * 2);
	}
	export function distance2Y(x: number, y: number, x1: number, y1: number): number {
		return (x - x1) * (x - x1) + (y - y1) * (y - y1) * 2;
	}
	// 使用最大值
	export function distanceMax(x: number, y: number, x1: number, y1: number): number {
		return Math.max(Math.abs(x - x1), Math.abs(y - y1) * sqrt2);
	}

	export function calcSpeed(x: number, y: number, px: number, py: number, time: number): number {
		// const x1 = px - x;
		// const y1 = py - y;
		const len = distanceY(x, y, px, py);
		return Math.floor((len * 1000) / time);
	}

	export function tileContain(
		tx: number,
		ty: number,
		tw: number,
		th: number,
		x: number,
		y: number,
	): boolean {
		return Math.abs((x - tx) * th * 0.5) + Math.abs((y - ty) * tw * 0.5) < tw * th * 0.25;
	}

	export function rectContain(
		l1: number,
		t1: number,
		r1: number,
		b1: number,
		x: number,
		y: number,
	): boolean {
		return x >= l1 && x <= r1 && y >= t1 && y <= b1;
	}
	export function rectCross(
		l1: number,
		t1: number,
		r1: number,
		b1: number,
		l2: number,
		t2: number,
		r2: number,
		b2: number,
	): boolean {
		const l = Math.max(l1, l2);
		const r = Math.min(r1, r2);
		const t = Math.max(t1, t2);
		const b = Math.min(b1, b2);
		return l <= r && t <= b;
	}
	export function rectCrossWH(
		l1: number,
		t1: number,
		w1: number,
		h1: number,
		l2: number,
		t2: number,
		w2: number,
		h2: number,
	): boolean {
		return rectCross(l1, t1, l1 + w1, t1 + h1, l2, t2, l2 + w2, t2 + h2);
	}

	const colors = [
		RGB(128, 255, 128), // A
		RGB(0x21, 0x86, 0xe9), // Blue
		RGB(0, 0, 0), // C
		RGB(0, 0, 0), // D
		RGB(255, 96, 64), // E
		RGB(0, 255, 64), // F
		RGB(0x0b, 0xd6, 0x32), // Green
		RGB(159, 50, 39), // H
		RGB(81, 83, 96), // I
		RGB(0xf2, 0x89, 0x00), // J 金
		RGB(0, 0, 0), // K
		RGB(0, 0, 0), // L
		RGB(0, 0, 0), // M
		RGB(0, 0, 0), // N
		colorName.橙色, // O
		RGB(0xfe, 0x4f, 0xd2), // Pink
		RGB(0, 0, 0), // Q
		RGB(0xee, 0x34, 0x1b), // Red
		RGB(0, 0, 0), // S
		RGB(60, 105, 113), // T
		RGB(81, 81, 96), // U
		RGB(49, 148, 142), // V
		RGB(255, 255, 255), // White
		RGB(128, 128, 128), // X
		RGB(0xfe, 0xed, 0x35), // Yellow
		colorName.蓝紫色, // 紫
	];

	const TblItemMsgColor = ['', '#G', '#B', '#Z', '#P', '#J', '#R', '#R'];
	export function getFormatColor(ch: string): number {
		if (ch[0] >= 'A' && ch[0] <= 'Z') {
			return colors[ch.charCodeAt(0) - 'A'.charCodeAt(0)];
		}
		return 0;
	}

	export function copy2clipboard(s: string): void {
		const input = document.createElement('input');
		input.value = s;
		document.body.appendChild(input);
		input.select();
		input.setSelectionRange(0, input.value.length);
		document.execCommand('Copy');
		document.body.removeChild(input);
	}

	export function isStringnull(s: string): boolean {
		return s === '' || s === null || s === undefined;
	}

	// export function obj_union(obj1:{},obj2:{}) : any{}
	export function maxString(word: string, maxnum: number): string {
		// let word : string = this.datas.word
		return `${word.substr(0, maxnum)}...`;
	}

	export function getNumberStartEnd(start: number, end: number, per: number): number {
		return start * per + end * (1 - per);
	}

	export function calcAnglePos(
		x: number,
		y: number,
		angle: number,
		dis: number,
	): [number, number] {
		if (!dis || dis === 0) {
			return [x, y];
		}
		const radius = (angle / 180) * Math.PI;
		return [x + dis * Math.cos(radius), y + dis * Math.sin(radius)];
	}

	export function calcDistancePosDir(
		x: number,
		y: number,
		dir: number,
		dis: number,
	): [number, number] {
		if (!dis || dis === 0) {
			return [x, y];
		}
		return dirMappos(x, y, dir, dis);
	}

	export function calcDistancePos(
		x: number,
		y: number,
		desx: number,
		desy: number,
		dis: number,
	): [number, number] {
		if (dis === 0) {
			return [desx, desy];
		}
		let x2;
		let y2;
		if (x > desx) {
			x2 = desx + dis;
		} else {
			x2 = desx - dis;
		}

		if (y > desy) {
			y2 = desy + dis / 2;
		} else {
			y2 = desy - dis / 2;
		}

		return [x2, y2];
	}

	// key like tree.ani
	export function getJsonByKeys(data: ObjectType, key: string): any {
		const keys = key.split('.');
		let curkey = keys[0];
		let next = 1;
		while (keys[next]) {
			data = data[curkey] as ObjectType;
			if (!data) {
				return null;
			}
			curkey = keys[next];
			next++;
		}
		return data[curkey];
	}

	// key like tree.ani
	export function updateJsonByKeys(data: ObjectType, key: string, v: unknown): any {
		function nextKey(d: any, key2: string): any {
			if (d[key2]) {
				return d[key2];
			}
			d[key2] = {};
			return d[key2];
		}

		const keys = key.split('.');
		let curkey = keys[0];
		let next = 1;
		while (keys[next]) {
			data = nextKey(data, curkey);
			curkey = keys[next];
			next++;
		}
		data[curkey] = v;
		return data;
	}

	// 特殊的额存档结构，其中只有一个数组！
	export function getArrayofobj(data: ObjectType): [string, any] {
		for (const [key, v] of Object.entries(data)) {
			// for (const key in data) {
			// const v = data[key];
			if (Array.isArray(v)) {
				return [key, v];
			}
			break;
		}
		return ['', null];
	}

	export function isObjectEmpty(obj: ObjectType): boolean {
		return !obj || Object.keys(obj).length === 0;
	}

	export function multiStrLen(Str: string): number {
		let len = 0;
		if (!Str || Str === '') return len;
		const str = Str;
		for (let i = 0; i < str.length; i++) {
			if (str.substr(i, 1).charCodeAt(0) > 255) len += 2;
			else len++;
		}
		return len;
	}

	export function color2Hex(cr: number): string {
		return cr.toString(16);
	}

	// 注意，这会导致初始化的一些方法比如：childrenCreated不被调用！
	export function cast<T>(obj: any, cl: { new(...args): T }): T {
		// eslint-disable-next-line no-proto
		obj.__proto__ = cl.prototype;
		return obj;
	}

	// 已知x1 < x2!
	// x,y在直线x1,y1,x2,y2的上方还是下方，注意x1,y1->x2,y2是向量
	export function pointAboveLine(
		x1: number,
		y1: number,
		x2: number,
		y2: number,
		x: number,
		y: number,
	) {
		return (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1) >= 0;
	}

	export function pointLeftLine(
		x1: number,
		y1: number,
		x2: number,
		y2: number,
		x: number,
		y: number,
	) {
		return (x2 - x1) * (y - y1) - (y2 - y1) * (x - x1) >= 0;
	}

	// 这个多变形必定是凸多边形！
	export function polygonContain(pos: number[], x: number, y: number): boolean {
		// const ax = Math.abs(x - pos[0]);
		// const ay = Math.abs(y - pos[1]);
		// return ax < 100 && ay < 100;
		const fpos = [];
		for (let i = 0; i < pos.length / 2; i++) {
			fpos.push([pos[2 * i], pos[2 * i + 1]]);
		}
		return inside([x, y], fpos);
	}

	// 这个多变形并不是凸多边形！
	export function polygonContain2(pos: number[][], x: number, y: number): boolean {
		// const ax = Math.abs(x - pos[0]);
		// const ay = Math.abs(y - pos[1]);
		// return ax < 100 && ay < 100;
		return inside([x, y], pos);
	}

	export function date2Save(d?: Date): string {
		if (!d) {
			d = new Date();
		}
		const y = d.getFullYear();
		const m = stringPad(d.getMonth() + 1, 2);
		const day = stringPad(d.getDate() + 1, 2);
		const h = stringPad(d.getHours() + 1, 2);
		const min = stringPad(d.getMinutes() + 1, 2);
		const s = stringPad(d.getSeconds() + 1, 2);
		return `${y}-${m}-${day}_${h}-${min}-${s}`;
	}
	export function date2Display(d?: Date): string {
		if (!d) {
			d = new Date();
		}
		const y = d.getFullYear();
		const m = stringPad(d.getMonth() + 1, 2);
		const day = stringPad(d.getDate() + 1, 2);
		const h = stringPad(d.getHours() + 1, 2);
		const min = stringPad(d.getMinutes() + 1, 2);
		const s = stringPad(d.getSeconds() + 1, 2);
		return `${y}-${m}-${day} ${h}:${min}:${s}`;
	}

	export function rotateR(
		x0: number,
		y0: number,
		x1: number,
		y1: number,
		delta: number,
	): [number, number] {
		const x = x1 - x0;
		const y = y1 - y0;

		const a = (delta / 180) * Math.PI;
		const cos = Math.cos(a);
		const sin = Math.sin(a);

		const x2 = x * cos - y * sin;
		const y2 = y * cos + x * sin;
		return [x2, y2];
	}

	export function scaleR(
		x0: number,
		y0: number,
		x1: number,
		y1: number,
		delta: number,
	): [number, number] {
		if (x0 === x1) {
			const x2 = x0;
			const dl = delta * Math.abs(y1 - y0);
			const y2 = y1 > y0 ? y1 + dl : y1 - dl;
			return [x2, y2];
		}
		const k = (y1 - y0) / (x1 - x0);
		const d = miscUtils.distance(x0, y0, x1, y1);
		const dl = (delta * d) / Math.sqrt(1 + k * k);
		// enlarge
		const x2 = x1 > x0 ? x1 + dl : x1 - dl;
		const y2 = x2 * k;
		return [x2, y2];
	}

	// 点到直线的距离！本来在直线上方正，下方为负！但是地图的y是颠倒的？
	export function pos2Line(
		x0: number,
		y0: number,
		x1: number,
		y1: number,
		x2: number,
		y2: number,
	): number {
		const d =
			((y2 - y1) * x0 + (x1 - x2) * y0 + (x2 * y1 - x1 * y2)) /
			Math.sqrt((y2 - y1) * (y2 - y1) + (x1 - x2) * (x1 - x2));
		return -d;
	}

	export function numberFix(n: number, bit: number) {
		return Math.floor(n * 10 ** bit) / 10 ** bit;
	}

	export function upperFirst(s: string): string {
		if (s.length === 0) {
			return s;
		}
		return s.substr(0, 1).toUpperCase() + s.substr(1);
	}
}
